package com.xf.system.utils.operationLog;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * @author xiefengf
 *
 * 日志切面类
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private DataSourceTransactionManager transactionManager;


    /**
     * 切入点
     * 统一切面日志
     * 参数校验、统一异常处理、日志打印
     */
    @Pointcut(value = "@annotation(com.xf.system.utils.operationLog.LogRecord)")
    public void pointcut() {
    }

    /**
     * 对带注解@LogRecord的方法进行切面，并获取到注解的属性值
     * 配置环绕通知
     * @param point 切入点
     */
    @Order(1)
    @Around(value = "pointcut() && @annotation(logRecord)")
    public Object doAfter(ProceedingJoinPoint point, LogRecord logRecord){
        // 手动开启事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        log.info("方法：" + point.getSignature() + "执行");
        Object proceed;
        try {
            // 1、手动前置通知

            // 2、执行增强方法
            // 获取方法的入参 -- 可以直接获取到当前方法的全部入参信息
            Object[] args = point.getArgs();
            System.out.println("args："+Arrays.toString(args));
            proceed = point.proceed(args);

            // 3、手动后置通知
            // 获取自定义注解
            LogRecord record = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(LogRecord.class);
            //获取注解上的SPEL表达式 并解析
            Integer num = SpelUtil.generateKeyBySpEL(record.num(), point,Integer.class);
            String username = SpelUtil.generateKeyBySpEL(record.username(), point,String.class);
            List strList = SpelUtil.generateKeyBySpEL(record.strList(),point,List.class);

            System.out.println("num："+num);
            System.out.println("username："+username);
            System.out.println("strList："+ strList);

            // 事务提交
            transactionManager.commit(status);
        } catch (Throwable e) {
            // 事务回滚
            transactionManager.rollback(status);
            throw new RuntimeException("执行项目规则操作记录切面失败"+e.getMessage());
        }
        //反射调用后的返回值也一定返回出去，不返回会空指针
        return proceed;
    }

}